/*
 * RangedValue.hpp
 *
 *  Created on: 17-11-2010
 *      Author: Przemek
 */

#pragma once

#include "utils/Range.hpp"

/*
 * Value which can only be in selected range. Assigning a non in range value trims the value to the
 * proper range. When the value is too low then the outcome value is equivalent to getMinimum()
 * and vice versa with too high value.
 */
template<typename T>
class RangedValue
{
	private:
		Range<T> range;
		T value;

	public:
		RangedValue(const Range<T>& range, const T& value);
		RangedValue(const Range<T>& range);
		RangedValue(const RangedValue<T>& other);

		RangedValue& operator=(const RangedValue<T>& other);
		const T& getValue() const;
		void setValue(const T& value);
		const Range<T>& getRange() const;
		float getFractionalPartInRange() const;
		T getValueFromFractionalPart(float fractionalPart) const;

		void changeBy(const T& amount);
		void setRange(const Range<T>& range);

	private:
		void clampValueToRange();
};

template<typename T>
RangedValue<T>::RangedValue(const Range<T>& range, const T& value) :
	range(range), value(value)
{
}

template<typename T>
RangedValue<T>::RangedValue(const Range<T>& range) :
	range(range), value(range.getMinimum())
{
}

template<typename T>
RangedValue<T>::RangedValue(const RangedValue<T>& other) :
	range(other.range), value(other.value)
{
}

template<typename T>
RangedValue<T>& RangedValue<T>::operator=(const RangedValue<T>& other)
{
	if(this != &other)
	{
		range = other.range;
		value = other.value;
	}

	return *this;
}

template<typename T>
const T& RangedValue<T>::getValue() const
{
	return value;
}

template<typename T>
void RangedValue<T>::setValue(const T& value)
{
	const T& difference = value - this->value;
	changeBy(difference);
}

template<typename T>
const Range<T>& RangedValue<T>::getRange() const
{
	return range;
}

template<typename T>
float RangedValue<T>::getFractionalPartInRange() const
{
	return (value - range.getMinimum()) / static_cast<float> (range.getExtent());
}

template<typename T>
T RangedValue<T>::getValueFromFractionalPart(float fractionalPart) const
{
	return static_cast<T> (fractionalPart * range.getExtent()) + range.getMinimum();
}

template<typename T>
void RangedValue<T>::changeBy(const T& amount)
{
	value += amount;

	clampValueToRange();
}

template<typename T>
void RangedValue<T>::setRange(const Range<T>& range)
{
	this->range = range;
	clampValueToRange();
}

template<typename T>
void RangedValue<T>::clampValueToRange()
{
	if(range.isAfterMaximum(value))
	{
		value = range.getMaximum();
	}
	else if(range.isBeforeMinimum(value))
	{
		value = range.getMinimum();
	}
}
